#include "ScanLineRasterizer.h"

ScanLineRasterizer::ScanLineRasterizer()
	:	AbstractRasterizer()
{
}
ScanLineRasterizer::~ScanLineRasterizer()
{
}


void ScanLineRasterizer::prepareForRender()
{
	mShaderGlobals.viewInv = mShaderGlobals.view;
	mShaderGlobals.viewInv.inverse();

	mVShaderParams.globals = &mShaderGlobals;
	
	mFShaderParams.globals = &mShaderGlobals;	

	cvZero(mShaderGlobals.renderTarget->depthBuffer);
	cvZero(mShaderGlobals.renderTarget->colorBuffer);
}
void ScanLineRasterizer::renderMesh(const Mesh& mesh)
{
	//mShaderGlobals.worldViewProj = mesh.transform * mShaderGlobals.view * mShaderGlobals.proj;
	mShaderGlobals.worldViewProj = mesh.transform * mShaderGlobals.view * mShaderGlobals.proj;
	mShaderGlobals.worldViewProj = mShaderGlobals.worldViewProj.transpose();
	mVShaderParams.material = mShaderGlobals.material;
	mFShaderParams.material = mShaderGlobals.material;
	for (size_t i = 0; i < mesh.faces.size(); i++){
		const Face& face = mesh.faces[i];
		drawTriangle(face.v);
	}
}


void ScanLineRasterizer::drawTriangle(const Vertex* v)
{
	double cp = 0.03125;//near clip plane??
	TransformedVertex vertices[3], newVertices[3];
	Vec3 ndc[3];

	for (int i = 0; i < 3; i++)
	{
		mVShaderParams.in = &v[i];
		mVShaderParams.out = &vertices[i];
		mVertexShader(&mVShaderParams);
	}

	//Skip triangles in front of the z clip plane and outside the camera frustum
	if (vertices[0].p.w >= cp && vertices[1].p.w >= cp && vertices[2].p.w >= cp){
		for (int j = 0; j < 3; j++){
			double invW = 1.0 / vertices[j].p.w;
			ndc[j].x = vertices[j].p.x * invW;
			ndc[j].y = vertices[j].p.y * invW;
			ndc[j].z = vertices[j].p.z * invW;
		}
		if(ndc[0].x < -1.0 && ndc[1].x < -1.0 && ndc[2].x < -1.0) return;
		if(ndc[0].x >  1.0 && ndc[1].x >  1.0 && ndc[2].x >  1.0) return;
		if(ndc[0].y < -1.0 && ndc[1].y < -1.0 && ndc[2].y < -1.0) return;
		if(ndc[0].y >  1.0 && ndc[1].y >  1.0 && ndc[2].y >  1.0) return;
		if(ndc[0].z < -1.0 && ndc[1].z < -1.0 && ndc[2].z < -1.0) return;
		if(ndc[0].z >  1.0 && ndc[1].z >  1.0 && ndc[2].z >  1.0) return;
	}

	// find 1 vertex or 2 vertices behind the z clip plane (cp must be close to 0.0 but greater than 0.0)
	int vtcc = 0, vtc, vntc; // vertices to clip count

	for(int j = 0; j < 3; j++)
	{
		if(vertices[j].p.w < cp)
		{
			vtcc++;
			vtc = j; // vertex behind the z clip plane
		}
		else
		{
			vntc = j; // vertex in front of the z clip plane
		}
	}

	if(vtcc == 3) return;

	double factor1, factor2;
	int v1, v2;
	Vec4 v1diff, v2diff;
	Real varying1diff[MAX_VARYINGS], varying2diff[MAX_VARYINGS];
	
	// clip the 2 vertices behind the z clip plane
	if(vtcc == 2)
	{
		v1 = (vntc + 1) % 3;

		v1diff.x = vertices[vntc].p.x - vertices[v1].p.x;
		v1diff.y = vertices[vntc].p.y - vertices[v1].p.y;
		v1diff.z = vertices[vntc].p.z - vertices[v1].p.z;
		v1diff.w = vertices[vntc].p.w - vertices[v1].p.w;

		factor1 = (cp - vertices[v1].p.w) / v1diff.w;

		vertices[v1].p.x = v1diff.x * factor1 + vertices[v1].p.x;
		vertices[v1].p.y = v1diff.y * factor1 + vertices[v1].p.y;
		vertices[v1].p.z = v1diff.z * factor1 + vertices[v1].p.z;
		vertices[v1].p.w = v1diff.w * factor1 + vertices[v1].p.w;

		for(int j = 0; j < MAX_VARYINGS; j++)
		{
			varying1diff[j] = vertices[vntc].varyings[j] - vertices[v1].varyings[j];
			vertices[v1].varyings[j] = varying1diff[j] * factor1 + vertices[v1].varyings[j];
		}

		v2 = (vntc + 2) % 3;

		v2diff.x = vertices[vntc].p.x - vertices[v2].p.x;
		v2diff.y = vertices[vntc].p.y - vertices[v2].p.y;
		v2diff.z = vertices[vntc].p.z - vertices[v2].p.z;
		v2diff.w = vertices[vntc].p.w - vertices[v2].p.w;

		factor2 = (cp - vertices[v2].p.w) / v2diff.w;

		vertices[v2].p.x = v2diff.x * factor2 + vertices[v2].p.x;
		vertices[v2].p.y = v2diff.y * factor2 + vertices[v2].p.y;
		vertices[v2].p.z = v2diff.z * factor2 + vertices[v2].p.z;
		vertices[v2].p.w = v2diff.w * factor2 + vertices[v2].p.w;

		for(int j = 0; j < MAX_VARYINGS; j++)
		{
			varying2diff[j] = vertices[vntc].varyings[j] - vertices[v2].varyings[j];
			vertices[v2].varyings[j] = varying2diff[j] * factor2 + vertices[v2].varyings[j];
		}
	}



	// clip the 1 vertex behind the z clip plane
	if(vtcc == 1)
	{
		for(int j = 0; j < 3; j++)
		{
			newVertices[j].p.x = vertices[j].p.x;
			newVertices[j].p.y = vertices[j].p.y;
			newVertices[j].p.z = vertices[j].p.z;
			newVertices[j].p.w = vertices[j].p.w;

			for(int k = 0; k < MAX_VARYINGS; k++)
			{
				newVertices[j].varyings[k] = vertices[j].varyings[k];
			}
		}

		v2 = (vtc + 2) % 3;

		v2diff.x = vertices[v2].p.x - vertices[vtc].p.x;
		v2diff.y = vertices[v2].p.y - vertices[vtc].p.y;
		v2diff.z = vertices[v2].p.z - vertices[vtc].p.z;
		v2diff.w = vertices[v2].p.w - vertices[vtc].p.w;

		factor2 = (cp - vertices[vtc].p.w) / v2diff.w;

		newVertices[v2].p.x = vertices[vtc].p.x = v2diff.x * factor2 + vertices[vtc].p.x;
		newVertices[v2].p.y = vertices[vtc].p.y = v2diff.y * factor2 + vertices[vtc].p.y;
		newVertices[v2].p.z = vertices[vtc].p.z = v2diff.z * factor2 + vertices[vtc].p.z;
		newVertices[v2].p.w = vertices[vtc].p.w = v2diff.w * factor2 + vertices[vtc].p.w;

		for(int j = 0; j < MAX_VARYINGS; j++)
		{
			varying2diff[j] = vertices[v2].varyings[j] - vertices[vtc].varyings[j];
			newVertices[v2].varyings[j] = vertices[vtc].varyings[j] = varying2diff[j] * factor2 + vertices[vtc].varyings[j];
		}

		v1 = (vtc + 1) % 3;

		v1diff.x = newVertices[v1].p.x - newVertices[vtc].p.x;
		v1diff.y = newVertices[v1].p.y - newVertices[vtc].p.y;
		v1diff.z = newVertices[v1].p.z - newVertices[vtc].p.z;
		v1diff.w = newVertices[v1].p.w - newVertices[vtc].p.w;

		factor1 = (cp - newVertices[vtc].p.w) / v1diff.w;

		newVertices[vtc].p.x = v1diff.x * factor1 + newVertices[vtc].p.x;
		newVertices[vtc].p.y = v1diff.y * factor1 + newVertices[vtc].p.y;
		newVertices[vtc].p.z = v1diff.z * factor1 + newVertices[vtc].p.z;
		newVertices[vtc].p.w = v1diff.w * factor1 + newVertices[vtc].p.w;

		for(int j = 0; j < MAX_VARYINGS; j++)
		{
			varying1diff[j] = newVertices[v1].varyings[j] - newVertices[vtc].varyings[j];
			newVertices[vtc].varyings[j] = varying1diff[j] * factor1 + newVertices[vtc].varyings[j];
		}
		rasterizeTriangle(newVertices);
	}
	rasterizeTriangle(vertices);
}

inline void setEdge(Fragment* f1, Fragment* f2, Edge* e)
{
	if (f1->fragCoord.y <= f2->fragCoord.y){
		e->f1 = f1;
		e->f2 = f2;
	}else{
		e->f1 = f2;
		e->f2 = f1;
	}
	e->diff = e->f2->fragCoord - e->f1->fragCoord;
	/*e->diff.x = e->f2->fragCoord.x - e->f1->fragCoord.x;
	e->diff.y = e->f2->fragCoord.y - e->f1->fragCoord.y;
	e->diff.z = e->f2->fragCoord.z - e->f1->fragCoord.z;
	e->wdiff = e->f2->fragCoord.w - e->f1->fragCoord.w;*/
	for (int i = 0; i < MAX_VARYINGS; i++)
	{
		e->varyingsdiff[i] = e->f2->varyings[i] - e->f1->varyings[i];
	}
}
inline void setSpan(Fragment* f1, Fragment* f2, Span* e)
{
	if (f1->fragCoord.x <= f2->fragCoord.x){
		e->f1 = f1;
		e->f2 = f2;
	}else{
		e->f1 = f2;
		e->f2 = f1;
	}
	e->diff = e->f2->fragCoord - e->f1->fragCoord;
	/*e->diff.x = e->f2->fragCoord.x - e->f1->fragCoord.x;
	e->diff.z = e->f2->fragCoord.z - e->f1->fragCoord.z;
	e->wdiff = e->f2->fragCoord.w - e->f1->fragCoord.w;*/
	for (int i = 0; i < MAX_VARYINGS; i++)
	{
		e->varyingsdiff[i] = e->f2->varyings[i] - e->f1->varyings[i];
	}
}


void ScanLineRasterizer::rasterizeTriangle(TransformedVertex* vertices)
{
	Fragment fragment[3];

	int w = mShaderGlobals.renderTarget->colorBuffer->width;
	int wmi1 = w-1;
	int h = mShaderGlobals.renderTarget->colorBuffer->height;
	int hmi1 = h-1;

	//project vertices into screen space
	for (int i = 0; i < 3; i++){
		double invW = 1.0 / vertices[i].p.w;
		vertices[i].p.w = invW;
		fragment[i].fragCoord.x = (vertices[i].p.x * invW * 0.5 + 0.5) * wmi1;
		fragment[i].fragCoord.y = (vertices[i].p.y * invW * 0.5 + 0.5) * hmi1;
	}

	//cull back-face triangles
	double a = 0.0;
	for ( int i = 0; i < 3; i++){
		//int mod = (i+1); //(i+1)%3
		//if (mod >= 3)
		//	mod -= 3;
		int mod = (i+1)%3;
		a += fragment[i].fragCoord.x * fragment[mod].fragCoord.y - fragment[mod].fragCoord.x * fragment[i].fragCoord.y;
	}
	a *= 0.5;
	if ( a < 0.0) return ;//for counter clockwise orientation, for clockwise the sign of a should be reversed

	//copy attributes
	for (int i = 0; i < 3; i++){
		Real w = vertices[i].p.w;
		fragment[i].fragCoord.z = vertices[i].p.z * w;
		fragment[i].fragCoord.w = w;
		for (int j = 0; j < MAX_VARYINGS; j++){
			fragment[i].varyings[j] = vertices[i].varyings[j] * w;
		}
	}


	// rasterize triangle with perspective correct z and attribute values interpolation
	// http://www.comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf
	// http://joshbeam.com/articles/triangle_rasterization/

	Edge edge[3];
	double maxLength = 0;
	int longEdge = 0;

	for (int i = 0; i < 3; i++)
	{
		int mod = (i+1)%3;
		setEdge(&fragment[i], &fragment[mod], &edge[i]);
		if (edge[i].diff.y > maxLength){
			maxLength = edge[i].diff.y;
			longEdge = i;
		}
	}
	int shortEdge1 = (longEdge + 1) % 3;
	int shortEdge2 = (longEdge + 2) % 3;

	drawSpanBetweenEdges(edge[longEdge], edge[shortEdge1]);
	drawSpanBetweenEdges(edge[longEdge], edge[shortEdge2]);
}

void ScanLineRasterizer::drawSpanBetweenEdges(Edge& e1, Edge& e2)
{
	if (e1.diff.y == 0.0 || e2.diff.y == 0.0) return ;

	int h = mShaderGlobals.renderTarget->colorBuffer->height;

	double step1 = 1.0 / e1.diff.y;
	double factor1 = (e2.f1->fragCoord.y - e1.f1->fragCoord.y) * step1;
	double step2 = 1.0 / e2.diff.y;
	double factor2 = 0.0;

	double y1 = e2.f1->fragCoord.y;
	double y2 = e2.f2->fragCoord.y;

	if (y1 < 0.5){
		double f = 0.5 - y1;
		factor1 += f * step1;
		factor2 += f * step2;
		y1 = 0.5;
	}else{
		double f = 0.5 - (y1 - (int)y1);
		factor1 += f * step1;
		factor2 += f * step2;
	}

	if (y2 > h)
		y2 = h;

	int iy1 = (int)y1;
	int iy2 = (int)y2;
	double f1, f2;
	Fragment frag1, frag2;
	Span span;

	for (int y = iy1; y < iy2; y++)
	{
		f1 = factor1 <= 0.0 ? 0.0 : factor1 >= 1.0 ? 1.0 : factor1;
		frag1.fragCoord = e1.diff * f1 + e1.f1->fragCoord;
		/*frag1.fragCoord.x = e1.diff.x * f1 + e1.f1->fragCoord.x;
		frag1.fragCoord.z = e1.zdiff * f1 + e1.f1->fragCoord.z;
		frag1.fragCoord.w = e1.wdiff * f1 + e1.f1->fragCoord.w;*/
		for (int i = 0; i < MAX_VARYINGS; i++)
			frag1.varyings[i] = e1.varyingsdiff[i] * f1 + e1.f1->varyings[i];

		f2 = factor2 <= 0.0 ? 0.0 : factor2 >= 1.0 ? 1.0 : factor2;
		frag2.fragCoord = e2.diff * f2 + e2.f1->fragCoord;
		/*frag2.fragCoord.x = e2.diff.x * f2 + e2.f1->fragCoord.x;
		frag2.fragCoord.z = e2.zdiff * f2 + e2.f1->fragCoord.z;
		frag2.fragCoord.w = e2.wdiff * f2 + e2.f1->fragCoord.w;*/
		for (int i = 0; i < MAX_VARYINGS; i++)
			frag2.varyings[i] = e2.varyingsdiff[i] * f2 + e2.f1->varyings[i];

		setSpan(&frag1, &frag2, &span);

		drawSpan(span, y);

		factor1 += step1;
		factor2 += step2;
	}
}

void ScanLineRasterizer::drawSpan(Span& span, int& y)
{
	if (span.diff.x == 0) return ;

	int w = mShaderGlobals.renderTarget->colorBuffer->width;

	double step = 1.0 / span.diff.x;
	double factor = 0.0;
	double x1 = span.f1->fragCoord.x;
	double x2 = span.f2->fragCoord.x;

	if (x1 < 0.5){
		factor += (0.5 - x1) * step;
		x1 = 0.5;
	}else{
		factor += (0.5 - (x1 - (int)x1)) * step;
	}

	if (x2 > w)
		x2 = w;

	int ix1 = (int)x1;
	int ix2 = (int)x2;
	double f;
	Fragment frag;
	
	DEPTH_IMAGE_FORMAT depth;
	DEPTH_IMAGE_FORMAT* depthBuffer = (w * y + ix1) + (DEPTH_IMAGE_FORMAT*)mShaderGlobals.renderTarget->depthBuffer->imageData;
	Vec4 fragColor;
	COLOR_IMAGE_FORMAT* colorBuffer = (w * y + ix1) * COLOR_BUFFER_CHANNELS + (COLOR_IMAGE_FORMAT*)mShaderGlobals.renderTarget->colorBuffer->imageData;

	for (int x = ix1; x < ix2; x++){
		f = factor <= 0.0 ? 0.0 : factor >= 1.0 ? 1.0 : factor;

		frag.fragCoord.z = span.diff.z * f + span.f1->fragCoord.z;

		if (frag.fragCoord.z >= -1.0 && frag.fragCoord.z <= 1.0){
			depth = (DEPTH_IMAGE_FORMAT)((frag.fragCoord.z * 0.5 + 0.5) * MAX_DEPTH_VALUE);
			if (*depthBuffer == 0 || depth <= *depthBuffer){
				frag.fragCoord.w = 1.0 / (span.diff.w * f + span.f1->fragCoord.w);
				for (int i = 0; i < MAX_VARYINGS; i++)
					frag.varyings[i] = (span.varyingsdiff[i] * f + span.f1->varyings[i]) * frag.fragCoord.w;

				mFShaderParams.fragment = &frag;
				mFragmentShader(&mFShaderParams);

				colorBuffer[2] = mFShaderParams.out.x <= 0.0 ? 0 : mFShaderParams.out.x >= 1.0 ? MAX_COLOR_VALUE : (COLOR_IMAGE_FORMAT)(mFShaderParams.out.x * MAX_COLOR_VALUE);
				colorBuffer[1] = mFShaderParams.out.x <= 0.0 ? 0 : mFShaderParams.out.y >= 1.0 ? MAX_COLOR_VALUE : (COLOR_IMAGE_FORMAT)(mFShaderParams.out.y * MAX_COLOR_VALUE);
				colorBuffer[0] = mFShaderParams.out.x <= 0.0 ? 0 : mFShaderParams.out.z >= 1.0 ? MAX_COLOR_VALUE : (COLOR_IMAGE_FORMAT)(mFShaderParams.out.z * MAX_COLOR_VALUE);

				*depthBuffer = depth;
			}
		}
		factor += step;
		depthBuffer++;
		colorBuffer += COLOR_BUFFER_CHANNELS;
	}

}